package com.hy.core;

import com.hy.core.util.ByteBufferUtils;
import com.hy.server.ServerHandler;
import com.hy.server.Session;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;

/**
 * author: houying
 * date  : 17-6-8
 * desc  :
 */
public class ReadCompletionHandler<P> implements CompletionHandler<Integer, Session> {

    private ServerHandler<P> handler;

    public ReadCompletionHandler(ServerHandler<P> handler) {
        this.handler = handler;
    }

    @Override
    public void completed(Integer result, Session session) {
        if (result > 0) {
            handle(session);
        } else if (result < 0) { //disconnected when reading
            try {
                System.out.println("disconnected while reading -> close");
                session.getSocketChannel().close();
                session.getReadByteBuffer().clear();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        ByteBuffer readByteBuffer = session.getReadByteBuffer();
        readByteBuffer.position(0);
        readByteBuffer.limit(readByteBuffer.capacity());
        session.getSocketChannel().read(readByteBuffer, session, this);
    }

    private void handle(Session session) {
        ByteBuffer byteBuffer = session.getReadByteBuffer();
        byteBuffer.flip();
        byteBuffer = composite(session.getLastByteBuffer(), byteBuffer);
        session.setLastByteBuffer(null);
        while(true) {
            int initPosition = byteBuffer.position();
            P pkg = handler.decode(byteBuffer, session);
            if (pkg == null) {
                ByteBuffer last = ByteBufferUtils.copy(byteBuffer, initPosition, byteBuffer.limit());
                session.setLastByteBuffer(last);
                return;
            } else {
                handler.handle(pkg);
            }
            if (byteBuffer.remaining() <= 0) {
                session.setLastByteBuffer(null);
                return;
            }
        }
    }

    private ByteBuffer composite(ByteBuffer lastByteBuffer, ByteBuffer byteBuffer) {
        if (lastByteBuffer == null) {
            return byteBuffer;
        }
        int capacity = (lastByteBuffer.limit() - lastByteBuffer.position()) + (byteBuffer.limit() - byteBuffer.position());
        ByteBuffer ret = ByteBuffer.allocate(capacity);

        ret.put(lastByteBuffer);
        ret.put(byteBuffer);

        ret.position(0);
        ret.limit(ret.capacity());
        return ret;
    }

    @Override
    public void failed(Throwable exc, Session session) {

    }
}
